package model

import (
	"errors"
	"github.com/eclipse/paho.mqtt.golang"
	"log"
	"time"
)

type BaseLink struct {
	opt          *mqtt.ClientOptions
	cli          mqtt.Client
	isMqttOnline bool //是否已经连接上mqtt服务器
	fnAfterLinkSuccess func() //连接成功后执行的函数
}

func (self *BaseLink) SetUp(conf Config) error {
	log.Println(conf)
	self.opt = mqtt.NewClientOptions()
	if conf.BrokerUrl == "" || conf.ClientId == "" || conf.UserName == "" {
		log.Println("错误：配置信息为空")
		return errors.New("错误：配置信息为空")
	}
	self.opt.AddBroker(conf.BrokerUrl)
	self.opt.SetClientID(conf.ClientId)
	self.opt.SetUsername(conf.UserName)
	self.opt.SetPassword(conf.PassWord)
	self.opt.SetKeepAlive(10*time.Second)
	self.opt.SetAutoReconnect(true) //断线后是否重新连接
	//self.opt.SetAutoReconnect(false) //这里我们用自己的逻辑进行重连
	//self.opt.SetCleanSession(false) //断线的时候是否还保留session ，如果需要重连必须设置为false
	self.opt.SetCleanSession(conf.IsCleanSession) //断线的时候是否还保留session ，如果需要重连必须设置为false
	self.opt.SetConnectionLostHandler(func(c mqtt.Client, e error) {
		self.isMqttOnline = false //标记失去连接
		log.Println("mqtt 丢失连接", e.Error())
	})
	self.opt.SetOnConnectHandler(func(c mqtt.Client) {
		self.isMqttOnline = true //标记失去连接
		log.Println("mqtt handler 连接成功--",conf.BrokerUrl)
		self.fnAfterLinkSuccess()
	})
	self.cli = mqtt.NewClient(self.opt)
	return nil
}

func (self *BaseLink) link() error {
	log.Println("mqtt base link")
	token := self.cli.Connect()
	ok := token.WaitTimeout(10 * time.Second) //使用gprs连接时时间较久所有设置长一点
	if !ok {
		self.isMqttOnline = false
		log.Println("mqtt 连接 超时")
		return errors.New("mqtt 连接 超时")
	}
	if token.Error() != nil {
		self.isMqttOnline = false
		log.Println("mqtt 连接 失败", token.Error())
		return token.Error()
	}
	self.isMqttOnline = true
	log.Println("mqtt 初始化成功")
	return nil
}

//启动一个线程专门用于断线后的重连
func (self *BaseLink) StartLinkWithKeepalive(lineokfunc func()) {
	self.fnAfterLinkSuccess=lineokfunc
	err := self.link()
	if err != nil {
		log.Println(err)
	}
	go func() {
		for {
			if !self.isMqttOnline {
				//重新初始化
				log.Println("监测到mqtt离线开始重连")
				self.Disconnect()
				self.cli = mqtt.NewClient(self.opt)
				err := self.link()
				if err != nil {
					log.Println(err)
				}
			}
			time.Sleep(30 * time.Second)
		}
	}()
}

func (self *BaseLink) Disconnect() {
	defer func() {
		if err := recover(); err != nil {
			log.Println("mqtt 断开连接失败可能还没初始化成功")
			log.Println(err)
		}
	}()
	self.cli.Disconnect(250)
}

//发布消息
func (self *BaseLink) MqttPub(topic string, msg []byte) error {
	if !self.isMqttOnline {
		log.Println("mqtt 服务还没连接上")
		return errors.New("mqtt 服务还没连接上")
	}
	token := self.cli.Publish(topic, 0, false, msg)
	ok := token.WaitTimeout(10 * time.Second)
	if !ok {
		log.Println("发布超时", token.Error())
		self.isMqttOnline = false
		if token.Error() != nil {
			self.isMqttOnline = false
			return errors.New("发布超时" + token.Error().Error())
		} else {
			self.isMqttOnline = false
			return errors.New("发布超时")
		}
	}
	if token.Error() != nil {
		self.isMqttOnline = false
		log.Println(token.Error())
		return token.Error()
	}
	return nil
}

//订阅消息
func (self *BaseLink) MqttSub(topic string, callback func(topic string, msg []byte)) error {
	if !self.isMqttOnline {
		log.Println("mqtt 服务还没连接上")
		return errors.New("mqtt 服务还没连接上")
	}
	var onMessageReceived mqtt.MessageHandler = func(client mqtt.Client, message mqtt.Message) {
		//log.Printf("Received message on topic: %s\nMessage: %s\n", message.Topic(), message.Payload())
		callback(message.Topic(), message.Payload())
	}
	token := self.cli.Subscribe(topic, 0, onMessageReceived)
	ok := token.WaitTimeout(10 * time.Second)
	if !ok {
		log.Println("订阅超时")
		self.isMqttOnline = false
		return errors.New("订阅超时")
	}
	if token.Error() != nil {
		log.Println(token.Error())
		self.isMqttOnline = false
		return token.Error()
	}
	return nil
}

//取消订阅
func (self *BaseLink) UnSub(topic ...string) error {
	if !self.isMqttOnline {
		log.Println("mqtt 服务还没连接上")
		return errors.New("mqtt 服务还没连接上")
	}
	token := self.cli.Unsubscribe(topic...)
	ok := token.WaitTimeout(5 * time.Second)
	if !ok {
		log.Println("取消订阅超时")
		return errors.New("取消订阅超时")
	}
	if token.Error() != nil {
		log.Println(token.Error())
		return token.Error()
	}
	return nil
}

func (self *BaseLink)GetIsMqttOnline()bool  {
	return self.isMqttOnline
}